﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using StateMachine;

namespace LilTmSim
{
	/// <summary>
	/// Interaction logic for StateNode.xaml
	/// </summary>
	public partial class StateNode : UserControl, INotifyPropertyChanged
	{
		public delegate void LocationChangedDelegate(StateNode sender);
		public event LocationChangedDelegate OnDragEnd;

		public delegate void OneStateNode(StateNode sender, bool value);
		public event OneStateNode OnIsInitialChanged;

		public event PropertyChangedEventHandler PropertyChanged;

		private static int _uid = 0;
		private static int uid { get { return _uid++; } }
		private static int getUid { get { return _uid; } }
		public State StateData { get; set; }
		public StateNodeCollection Container { get; set; }

		/// <summary>
		/// Gets or sets the Canvas.Left position of this element
		/// </summary>
		public double X
		{
			get
			{
				return Canvas.GetLeft(this);
			}
			set
			{
				if (value != X)
				{
					OldLocation = new Point(X, Y);
					Canvas.SetLeft(this, value);
					if (PropertyChanged != null)
					{
						PropertyChanged(this, new PropertyChangedEventArgs("X"));
						PropertyChanged(this, new PropertyChangedEventArgs("Location"));
					}
					StateData.Location = new Point(X, Y);
				}
			}
		}

		/// <summary>
		/// Gets or sets the Canvas.Top position of this element
		/// </summary>
		public double Y
		{
			get
			{
				return Canvas.GetTop(this);
			}
			set
			{
				if (value != Y)
				{
					OldLocation = new Point(X, Y);
					Canvas.SetTop(this, value);
					if (PropertyChanged != null)
					{
						PropertyChanged(this, new PropertyChangedEventArgs("Y"));
						PropertyChanged(this, new PropertyChangedEventArgs("Location"));
					}
					StateData.Location = new Point(X, Y);
				}
			}
		}

		/// <summary>
		/// Name is a Dependency Property.
		/// </summary>
		public string Name
		{
			get { return (string)GetValue(NameProperty); }
			set { SetValue(NameProperty, value); }
		}
		public static readonly DependencyProperty NameProperty =
			DependencyProperty.Register("Name", typeof(string), typeof(StateNode), new UIPropertyMetadata("", new PropertyChangedCallback(NameChanged)));

		private static void NameChanged(DependencyObject o, DependencyPropertyChangedEventArgs a)
		{
			StateNode n = (o as StateNode);
			n.StateData.Name = n.Name;
		}

		/// <summary>
		/// TransitionsLeaving is a Dependency Property.
		/// </summary>
		public TransitionNodeCollection TransitionsLeaving
		{
			get { return (TransitionNodeCollection)GetValue(TransitionsLeavingProperty); }
			set { SetValue(TransitionsLeavingProperty, value); }
		}
		public static readonly DependencyProperty TransitionsLeavingProperty =
			DependencyProperty.Register("TransitionsLeaving", typeof(TransitionNodeCollection), typeof(StateNode), new UIPropertyMetadata(null, new PropertyChangedCallback(TransitionsLeavingChanged)));

		static void TransitionsLeavingChanged(DependencyObject o, DependencyPropertyChangedEventArgs a)
		{
			StateNode n = o as StateNode;
			n.StateData.Transitions = new TransitionCollection(n.TransitionsLeaving.TransitionDataList);
		}


		/// <summary>
		/// TransitionsEntering is a Dependency Property.
		/// </summary>
		public TransitionNodeCollection TransitionsEntering
		{
			get { return (TransitionNodeCollection)GetValue(TransitionsEnteringProperty); }
			set { SetValue(TransitionsEnteringProperty, value); }
		}
		public static readonly DependencyProperty TransitionsEnteringProperty =
			DependencyProperty.Register("TransitionsEntering", typeof(TransitionNodeCollection), typeof(StateNode), new UIPropertyMetadata(null));


		/// <summary>
		/// Label is a Dependency Property.
		/// </summary>
		public string Label
		{
			get { return (string)GetValue(LabelProperty); }
			set { SetValue(LabelProperty, value); }
		}
		public static readonly DependencyProperty LabelProperty =
			DependencyProperty.Register("Label", typeof(string), typeof(StateNode), new UIPropertyMetadata("", new PropertyChangedCallback(LabelChanged)));

		static void LabelChanged(DependencyObject o, DependencyPropertyChangedEventArgs a)
		{
			StateNode n = o as StateNode;
			n.StateData.Label = n.Label;
		}

		public Point Location
		{
			get { return new Point(X, Y); }
			set
			{
				if (Location != value)
				{
					X = value.X;
					Y = value.Y;

					if (PropertyChanged != null)
						PropertyChanged(this, new PropertyChangedEventArgs("Location"));

					StateData.Location = Location;
				}
			}
		}

		/// <summary>
		/// Gets the unique id of this node. Id is a Dependency Property.
		/// </summary>
		public int Id
		{
			get { return (int)GetValue(IdProperty); }
			set { SetValue(IdProperty, value); }
		}
		public static readonly DependencyProperty IdProperty =
			DependencyProperty.Register("Id", typeof(int), typeof(StateNode), new UIPropertyMetadata(-1, new PropertyChangedCallback(IdChanged)));

		static void IdChanged(DependencyObject o, DependencyPropertyChangedEventArgs a)
		{
			StateNode n = o as StateNode;
			n.StateData.Id = n.Id.ToString();
		}

		/// <summary>
		/// IsSelected is a Dependency Property.
		/// </summary>
		public bool IsSelected
		{
			get { return (bool)GetValue(IsSelectedProperty); }
			set { SetValue(IsSelectedProperty, value); }
		}
		public static readonly DependencyProperty IsSelectedProperty =
			DependencyProperty.Register("IsSelected", typeof(bool), typeof(StateNode), new UIPropertyMetadata(false));

		/// <summary>
		/// IsFinal is a Dependency Property.
		/// </summary>
		public bool IsFinal
		{
			get { return (bool)GetValue(IsFinalProperty); }
			set { SetValue(IsFinalProperty, value); }
		}
		public static readonly DependencyProperty IsFinalProperty =
			DependencyProperty.Register("IsFinal", typeof(bool), typeof(StateNode), new UIPropertyMetadata(false, new PropertyChangedCallback(IsFinalChanged)));

		static void IsFinalChanged(DependencyObject o, DependencyPropertyChangedEventArgs a)
		{
			StateNode n = o as StateNode;
			n.StateData.IsFinal = n.IsFinal;
		}

		/// <summary>
		/// IsInitial is a Dependency Property.
		/// </summary>
		public bool IsInitial
		{
			get { return (bool)GetValue(IsInitialProperty); }
			set { SetValue(IsInitialProperty, value); }
		}
		public static readonly DependencyProperty IsInitialProperty =
			DependencyProperty.Register("IsInitial", typeof(bool), typeof(StateNode), new UIPropertyMetadata(false, new PropertyChangedCallback(IsInitialChanged)));

		static void IsInitialChanged(DependencyObject o, DependencyPropertyChangedEventArgs a)
		{
			StateNode n = (o as StateNode);
			if (n.OnIsInitialChanged != null)
			{
				n.OnIsInitialChanged(o as StateNode, n.IsInitial);
			}
			n.StateData.IsInitial = n.IsInitial;
		}

		/// <summary>
		/// ShowLabel is a Dependency Property.
		/// </summary>
		public bool ShowLabel
		{
			get { return (bool)GetValue(ShowLabelProperty); }
			set { SetValue(ShowLabelProperty, value); }
		}
		public static readonly DependencyProperty ShowLabelProperty =
			DependencyProperty.Register("ShowLabel", typeof(bool), typeof(StateNode), new UIPropertyMetadata(false, new PropertyChangedCallback(ShowLabelChanged)));

		static void ShowLabelChanged(DependencyObject o, DependencyPropertyChangedEventArgs a)
		{
			StateNode n = o as StateNode;
			n.StateData.ShowLabel = n.ShowLabel;
		}

		/// <summary>
		/// IsTracing is a Dependency Property.
		/// </summary>
		public bool IsTracing
		{
			get { return (bool)GetValue(IsTracingProperty); }
			set { SetValue(IsTracingProperty, value); }
		}
		public static readonly DependencyProperty IsTracingProperty =
			DependencyProperty.Register("IsTracing", typeof(bool), typeof(StateNode), new UIPropertyMetadata(false));

		/// <summary>
		/// OldLocation is a Dependency Property.
		/// </summary>
		public Point OldLocation
		{
			get { return (Point)GetValue(OldLocationProperty); }
			set { SetValue(OldLocationProperty, value); }
		}
		public static readonly DependencyProperty OldLocationProperty =
			DependencyProperty.Register("OldLocation", typeof(Point), typeof(StateNode), new UIPropertyMetadata(null));

		public StateNode() : this(uid, new State(getUid.ToString())) { }
		public StateNode(State stateData) : this(uid, stateData) { }
		public StateNode(int id, State stateData)
		{
			StateData = stateData;
			Id = id;
			Name = "q" + Id.ToString();
			Label = "N/A";

			Mouse.AddMouseDownHandler(this, StateNode_DragStart);

			Loaded += delegate
			{
				Renamer.DeleteClicked += delegate { Container.Remove(this); };
			};

			TransitionsEntering = new TransitionNodeCollection();
			TransitionsLeaving = new TransitionNodeCollection();

			InitializeContextMenu();
			InitializeComponent();
		}

		private void InitializeContextMenu()
		{
			ContextMenu = new ContextMenu();
			ContextMenu.Items.Add(AddMenuItem("Show Label", null, "ShowLabel"));
			ContextMenu.Items.Add(new Separator());
			ContextMenu.Items.Add(AddMenuItem("Initial", null, "IsInitial"));
			ContextMenu.Items.Add(AddMenuItem("Final", null, "IsFinal"));
			ContextMenu.Items.Add(new Separator());
			ContextMenu.Items.Add(AddMenuItem("Delete", delegate { Container.Remove(this); }));
		}

		private MenuItem AddMenuItem(string title, RoutedEventHandler onClick) { return AddMenuItem(title, onClick, null); }
		private MenuItem AddMenuItem(string title, RoutedEventHandler onClick, string prop)
		{
			MenuItem item = new MenuItem()
			{
				Header = title,
				IsCheckable = !String.IsNullOrEmpty(prop),
			};
			if (item.IsCheckable)
				item.SetBinding(MenuItem.IsCheckedProperty, new Binding() { Source = this, Path = new PropertyPath(prop) });
			else
				item.Click += onClick;
			return item;
		}

		Point mouseOffset;
		void StateNode_DragStart(object sender, MouseEventArgs e)
		{
			if (!Keyboard.IsKeyDown(Key.LeftCtrl))
			{
				mouseOffset = e.GetPosition(this as IInputElement);
				Mouse.AddMouseMoveHandler(this, StateNode_DragMove);
				Mouse.AddMouseUpHandler(this, StateNode_DragEnd);
				this.CaptureMouse();
			}
		}

		void StateNode_DragMove(object sender, MouseEventArgs e)
		{
			Point pos = Mouse.GetPosition((IInputElement)Parent);
			this.X = pos.X - mouseOffset.X;
			this.Y = pos.Y - mouseOffset.Y;
		}

		void StateNode_DragEnd(object sender, MouseEventArgs e)
		{
			this.ReleaseMouseCapture();
			Mouse.RemoveMouseMoveHandler(this, StateNode_DragMove);
			Mouse.RemoveMouseUpHandler(this, StateNode_DragEnd);

			if (OnDragEnd != null)
				OnDragEnd(this);
		}

		private void ChangeLabel()
		{
			MessageBox.Show("Change Label Not Implemented");
		}

		public override string ToString()
		{
			return Name;
		}

	}
}
